﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using pwestcott.Util;

namespace pwestcott.Utils.PerformanceTests.Essence
{
    class CompareToTests : ITest
    {
        interface IMyData
        {
            int MyInt { get; set; }
            string MyString { get; set; }
            DateTime MyDateTime { get; set; }
        }

        [EssenceClass(UseIn = EssenceFunctions.All)]
        class TestEssenceStatic : IComparable<TestEssenceStatic>
        {
            static readonly Func<TestEssenceStatic, TestEssenceStatic, int> _compareTo = Essence<TestEssenceStatic>.CompareToStatic;

            [Essence(Order = 0)] public int MyInt { get; set; }
            [Essence(Order = 1)] public string MyString { get; set; }
            [Essence(Order = 2)] public DateTime MyDateTime { get; set; }

            public int CompareTo(TestEssenceStatic other)
            {
                return _compareTo(this, other);
            }
        }

        class TestEssenceMember : IComparable<TestEssenceMember>
        {
            private readonly Func<TestEssenceMember, int> _compareTo;

            [Essence(Order = 0)] public int MyInt { get; set; }
            [Essence(Order = 1)] public string MyString { get; set; }
            [Essence(Order = 2)] public DateTime MyDateTime { get; set; }

            public TestEssenceMember()
            {
                _compareTo = Essence<TestEssenceMember>.CompareToInstance(this);
            }

            public int CompareTo(TestEssenceMember other)
            {
                return _compareTo(other);
            }
        }

        class TestManual : IComparable<TestManual>
        {
            public int MyInt { get; set; }
            public string MyString { get; set; }
            public DateTime MyDateTime { get; set; }

            public int CompareTo(TestManual other)
            {
                if (other == null)
                    return -1;
                if (ReferenceEquals(this, other))
                    return 0;

                int result;
                result = MyInt.CompareTo(other.MyInt);
                if (result != 0)
                    return result;
                if (MyString == null)
                {
                    if (other.MyString != null)
                        return 1;
                }
                else
                {
                    result = MyString.CompareTo(other.MyString);
                    if (result != 0)
                        return result;
                }
                result = MyDateTime.CompareTo(other.MyDateTime);
                if (result != 0)
                    return result;

                return 0;
            }
        }

        public int TestObj<T>(string name, int iterations, T x1, T x2, T x3)
            where T : IComparable<T>
        {
            int total = 0;

            DateTime start = DateTime.Now;
            for (int i = 0; i < iterations; ++i)
            {
                unchecked
                {
                    if (x1.CompareTo(x1) == 0)
                        ++total;
                    if (x1.CompareTo(x2) == 0)
                        ++total;
                    if (x2.CompareTo(x3) == 0)
                        ++total;
                }
            }
            DateTime end = DateTime.Now;

            Console.WriteLine("{0} {1}", name, (end - start).TotalMilliseconds);

            return total;
        }

        public void Run()
        {
            const int iterations = 10000000;

            var now = DateTime.Now;

            var staticTest = new TestEssenceStatic();
            var memberTest = new TestEssenceMember();
            var manualTest = new TestManual();

            staticTest.MyDateTime = memberTest.MyDateTime = manualTest.MyDateTime = now;
            staticTest.MyInt = memberTest.MyInt = manualTest.MyInt = 42;
            staticTest.MyString = memberTest.MyString = manualTest.MyString = "The Quick Brown Fox Jumps Over The Lazy Dog";

            var staticTestSame = new TestEssenceStatic();
            var memberTestSame = new TestEssenceMember();
            var manualTestSame = new TestManual();

            staticTestSame.MyDateTime = memberTestSame.MyDateTime = manualTestSame.MyDateTime = now;
            staticTestSame.MyInt = memberTestSame.MyInt = manualTestSame.MyInt = 42;
            staticTestSame.MyString = memberTestSame.MyString = manualTestSame.MyString = "The Quick Brown Fox Jumps Over The Lazy Dog";

            var staticTestDifferent = new TestEssenceStatic();
            var memberTestDifferent = new TestEssenceMember();
            var manualTestDifferent = new TestManual();

            staticTestDifferent.MyDateTime = memberTestDifferent.MyDateTime = manualTestDifferent.MyDateTime = new DateTime(2000, 1, 1);
            staticTestDifferent.MyInt = memberTestDifferent.MyInt = manualTestDifferent.MyInt = 16;
            staticTestDifferent.MyString = memberTestDifferent.MyString = manualTestDifferent.MyString = null;

            var sanity1 = TestObj("static", iterations, staticTest, staticTestSame, staticTestDifferent);
            var sanity3 = TestObj("member", iterations, memberTest, memberTestSame, memberTestDifferent);
            var sanity2 = TestObj("manual", iterations, manualTest, manualTestSame, manualTestDifferent);

            if (sanity1 == sanity2 && sanity1 == sanity3)
                Console.WriteLine("Sanity check passed ({0})", sanity1);
            else
                Console.WriteLine("Sanity check failed ({0}, {1}, {2})", sanity1, sanity2, sanity3);
        }
    }
}
